﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using CSA.Lib;

namespace CSA.Operator
{
    public class MutationOperator : ICSOperator
    {

        #region Fields
        CSPopulation m_OperatePopulation=null;
        String m_CurUsedFunctionName = "";
        Hashtable m_FunctionNames = new Hashtable();    
        #endregion

        #region Properties

        public String Name
        {
            get { return "MutationOperator"; }
        }

        public String Caption
        {
            get { return global::CSA.Operator.TextString.MutationOperatorCaption; }
        }

        public String Description
        {
            get { return global::CSA.Operator.TextString.MutationOperatorDes; }
        }

        public String Library
        {
            get { return "CSA.Operator"; }
        }

        public Hashtable Get_Parameters(String nFunctionName)
        {
            String nValidateName = "";
            if (nFunctionName.Trim() == "" || !m_FunctionNames.Contains(nFunctionName))
            {
                nValidateName = "DefaultMuation";

            }
            else
            {
                nValidateName = nFunctionName;
            }
            CSFuntionInfo nDefulatParas = (CSFuntionInfo)m_FunctionNames[nValidateName];
            return nDefulatParas.Parameters;
        }

        public OperatorType OperatorType
        {
            get { return OperatorType.enumMutationOperator; }
        }

        public Hashtable FunctionNames
        {
            get { return m_FunctionNames; }
        }

        public bool UseCustomAntibodyOperator
        {
            get { return false; }
        }

        public CSFuntionInfo CustomAntibodyOperator
        {
            get { return null; }
            set { }
        }

        #endregion

        #region Methods

        public MutationOperator()
        {
            InitialParameters();
        }
        public bool Execute(CSPopulation nOperatePopulation, ref String nErrorStr, String nMethodName = "")
        {
            m_OperatePopulation = nOperatePopulation;
            if (nMethodName.Trim() == "" || !m_FunctionNames.Contains(nMethodName))
            {
                m_CurUsedFunctionName = "DefaultMuation";
            }
            else
            {
                m_CurUsedFunctionName = nMethodName;
            }
            if (m_CurUsedFunctionName == "DefaultMuation")
            {
                return DefaultMutate(ref nErrorStr);
            }
            else if (m_CurUsedFunctionName == "AdaptiveMutation")
            {
                return AdaptiveMutation(ref nErrorStr);
            }
            else if (m_CurUsedFunctionName == "MutateAsFixedRate")
            {
                return MutateAsFixedRate(ref nErrorStr);
            }
            return true;
        }

        private bool MutateAsFixedRate(ref String nErrorStr)
        {
            try
            {
                CSFuntionInfo nDefulatParas = (CSFuntionInfo)m_FunctionNames[m_CurUsedFunctionName];
                ICSOptimizationProblem nProblem = m_OperatePopulation.ProblemInfo;
                List<CSAntibody> nNewPop = m_OperatePopulation.NewAntibodies;
                CSParameter MatationRate = (CSParameter)nDefulatParas.Parameters["MutateRate"];
                Double nMutationCoe = Convert.ToDouble(MatationRate.Value.ToString());
                Int32 nIt;
                for (nIt = 0; nIt < nNewPop.Count; ++nIt)
                {
                    CSAntibody nAb = nNewPop[nIt];
                    nProblem.MutateAb(ref nAb, nMutationCoe);
                }
                nNewPop.Sort(new AntibodyCompare());
                return true;
            }
            catch (Exception ex)
            {
                nErrorStr = ex.Message;
                return false;
            }
        }

        private bool AdaptiveMutation(ref String nErrorStr)
        {
            try
            {
                ICSOptimizationProblem nProblem = m_OperatePopulation.ProblemInfo;
                List<CSAntibody> nNewPop = m_OperatePopulation.NewAntibodies;
                nNewPop.Sort(new AntibodyCompare());
                Double nMaxAffinity = nNewPop[0].Affinity;
                Double nMinAffinity = nNewPop[nNewPop.Count - 1].Affinity;
                Double nRange = nMaxAffinity - nMinAffinity;
                Int32 nIt;
                for (nIt = 0; nIt < nNewPop.Count; ++nIt)
                {
                    CSAntibody nAb = nNewPop[nIt];
                    Double nMutationCoe = (nAb.Affinity - nMinAffinity) / nRange;
                    nMutationCoe = Math.Exp(-2 * nMutationCoe) / (m_OperatePopulation.CurGeneration+1);
                    nProblem.MutateAb(ref nAb, nMutationCoe);
                }
                return true;
            }
            catch (Exception ex)
            {
                nErrorStr = ex.Message;
                return false;
            }
        }

        /// <summary>
        /// The mutation rate is determined based on the affinity of each antibody: the higher the affinity, the smaller the mutation rate.
        /// </summary>
        /// <param name="nErrorStr"></param>
        /// <returns></returns>
        private bool DefaultMutate(ref String nErrorStr)
        {
            try
            {             
                ICSOptimizationProblem nProblem = m_OperatePopulation.ProblemInfo;
                List<CSAntibody> nNewPop = m_OperatePopulation.NewAntibodies;
                nNewPop.Sort(new AntibodyCompare());
                Double nMaxAffinity = nNewPop[0].Affinity;
                Double nMinAffinity = nNewPop[nNewPop.Count-1].Affinity;
                Double nRange = nMaxAffinity - nMinAffinity;
                Int32 nIt;
                for (nIt = 0; nIt < nNewPop.Count; ++nIt)
                {
                    CSAntibody nAb = nNewPop[nIt];
                    Double nMutationCoe = (nAb.Affinity - nMinAffinity) / nRange;
                    nMutationCoe = Math.Exp(-2 * nMutationCoe);
                    nProblem.MutateAb(ref nAb, nMutationCoe);
                } 
                return true;
            }
            catch (Exception ex)
            {
                nErrorStr = ex.Message;
                return false;
            }
        }

        public void InitialParameters()
        {
            m_FunctionNames.Clear();
            //There are three mutate functions' information.
            //Default mutation function information 
            CSFuntionInfo nMutateFunction = new CSFuntionInfo();
            nMutateFunction.Caption = global::CSA.Operator.TextString.DefaultFunction;
            nMutateFunction.Description = global::CSA.Operator.TextString.DefaultMutateFunctionDes;
            nMutateFunction.Name = "DefaultMuation";
            m_FunctionNames.Add(nMutateFunction.Name, nMutateFunction);

            //Adaptive mutation function
            CSFuntionInfo nAdapMutateFunction = new CSFuntionInfo();
            nAdapMutateFunction.Caption = global::CSA.Operator.TextString.AdaptiveMutation;
            nAdapMutateFunction.Description = global::CSA.Operator.TextString.AdaptiveMutationDes;
            nAdapMutateFunction.Name = "AdaptiveMutation";
            m_FunctionNames.Add(nAdapMutateFunction.Name, nAdapMutateFunction);

            ///The same rate
            CSFuntionInfo nMFAsSameMutateRate = new CSFuntionInfo();
            nMFAsSameMutateRate.Caption = global::CSA.Operator.TextString.MutateAsFixedRateFunction;
            nMFAsSameMutateRate.Description = global::CSA.Operator.TextString.MutateAsFixedRate;
            nMFAsSameMutateRate.Name = "MutateAsFixedRate";
            Hashtable nStandardFunctionPara = new Hashtable();

            float nDefulatPMute = 0.1F;  //
            CSParameter nPMuterate = new CSParameter();
            nPMuterate.Type = ParameterType.enumFloat;
            nPMuterate.Caption = global::CSA.Operator.TextString.MutateRate;
            nPMuterate.Description = global::CSA.Operator.TextString.MutateRateDes;
            nPMuterate.Name = "MutateRate";
            nPMuterate.Value = nDefulatPMute;
            nPMuterate.DomainType = enumParasDomain.enumDomainExtent;

            string low1 = "0.001";
            string up1 = "0.8";
            List<string> nDomains1 = new List<string>();
            nDomains1.Add(low1);
            nDomains1.Add(up1);
            nPMuterate.DomainValues = nDomains1;
            nStandardFunctionPara.Add("MutateRate", nPMuterate);
            nMFAsSameMutateRate.Parameters = nStandardFunctionPara;
            m_FunctionNames.Add(nMFAsSameMutateRate.Name, nMFAsSameMutateRate);          

        }
        #endregion
    }
}
